The TADS Alternate Library
Version 2.0

Getting Started


Copyright 2000 by Kevin Forchione.
This is part of the TADS Alternate Library Authors Manual.

Introduction and Table of Contents




Building A Sample Game

 

It has been said that the best way to learn is by example. In this chapter we begin building a sample game that will serve to illustrate several of the features of the Alt library.

 

First, open a text-editor and copy the following, saving the file as ASCII text. Name the file venture.t and place it in a folder named Venture.

 

#include <stdif.h>

 

modify story

    startingLocation = forestClearing

    title = "VENTURE"

    headline = "An Interactive Worked Example\n

        Copyright (c) 2000 by Kevin Forchione\n"

    introduction = "\b\b\b\bDays of cutting through impenetrable

        underbrush have brought you to the brink of exhaustion. At last

        your efforts bear fruit as you stumble upon the remnants of a

        lost civilisation."

;

 

forestClearing: Room

    sDesc = "Forest Clearing"

    lDesc = "This small clearing is covered in a thick bed of grasses

        and heavy frond leaves of the trees that form a near-

        impenetrable barrier encircling it. The air is heavy and

        sweltering, seemingly drawing out swarms of tiny blue-bottle

        flies that dart about drawn to the rotting vegetation."

;

 

Because Alt supports HTML status and prompt by default there is no need to #define USE_HTML_STATUS and USE_HTML_PROMPT at the beginning of your game file. If you wish to compile your game with non-HTML support then you will need to modify story.initCommon() and remove the #defines from the stdif header file.

 

Compile this file using the TADS workbench. You will need to set the #include paths in the workbench: Build -> settings, then choose the include tab. Using the Add button include the following directories:

 

alt

alt\classes

alt\functions

alt\grammar

alt\objects

 

The full path names will depend upon your system, and where you put the Alt library folder. Once you’ve applied these additional include libraries then you’ll be ready to compile and run.

 

 

Days of cutting through impenetrable underbrush have brought you to the brink of exhaustion. At last your efforts bear fruit as you stumble upon the remnants of a lost civilisation.

 

VENTURE

An Interactive Worked Example

Copyright (c) 2000 by Kevin Forchione

Release 1 / Serial number 000330

Alt Library Version 1.0.2

 

Forest Clearing

       This small clearing is covered in a thick bed of grasses and heavy frond leaves of the trees that form a near- impenetrable barrier encircling it. The air is heavy and sweltering, seemingly drawing out swarms of tiny blue-bottle flies that dart about drawn to the rotting vegetation.

 

>i

You are empty-handed.

 

>north

You can't go that way.

 

>wait

Time passes...

 

>quit

In a total of 15 minutes, you have achieved a score of 0 points out of a possible 100.

 

Do you really want to quit? (YES or NO) > yes

 

The release number defaults to 1, but is easily changed by modifying the story objects release attribute:

 

       modify story

              release = 2

       ;

 

Version information is automatically generated at compile time and consists of the compilation date in the format YYMMDD.

 

Next we’ll add an object to our forestClearing simply by coding the following after the forestClearing definition.

 

redBerry: FoodItem

    location = forestClearing

    noun = 'berry'

    adjective = 'red'

    sDesc = "red berry"

;

 

The redBerry is an instance of FoodItem class, which means that it inherits methods that allow an actor to eat it. The berry has a location attribute that indicates that it is to be found in the forestClearing; an sDesc (short description) that is used to describe the berry in listings; as well as noun and adjective vocabulary attributes, which allow the berry to be referenced by the parser as part of a player command.

 

The room description now displays the line “You see a red berry here.” after the room’s lDesc (long description). We can embellish this by replacing the above with the following definition.

 

redBerry: FoodItem

    location = forestClearing

    noun = 'berry'

    adjective = 'red'

    sDesc = "red berry"

    initial = "A red berry hangs suspended from a vine

        like a single drop of blood."

;

 

Our redBerry definition now includes an initial attribute. The initial attribute is displayed in room descriptions before the object has been taken or moved. Our room description now appears as follows:

 

Forest Clearing

       This small clearing is covered in a thick bed of grasses and heavy frond leaves of the trees that form a near- impenetrable barrier encircling it. The air is heavy and sweltering, seemingly drawing out swarms of tiny blue-bottle flies that dart about drawn to the rotting vegetation.

       A red berry hangs suspended from a vine like a single drop of blood.

           

But our berry simply displays “It looks like an ordinary red berry to me.” when we examine or look at it. To change this response we need to give our definition an lDesc (long description).

 

redBerry: FoodItem

    location = forestClearing

    noun = 'berry'

    adjective = 'red'

    sDesc = "red berry"

    initial = "A red berry hangs suspended from a vine

        like a single drop of blood."

    lDesc = "The berry gleams darkly ruby-red, leaving you

        to wonder what it is you seem to have forgotten about

        this particular fruit. "

;

 

Now our berry has taken on a slightly sinister aspect. But FoodItem class objects allow an actor to eat an instance of its class without reticence.

 

>take berry

Taken.

 

>x berry

The berry gleams darkly ruby-red, leaving you to wonder what it is you seem to have forgotten about this species.

 

>eat berry

That was delicious!

 

Suppose we want to make special comment concerning the taking of the berry, rather than the default “Taken.” display. Alt implements postAction methods that allow you to replace the default display without having to modify the class’ doEat() method. To illustrate, we’ll vary the <<take berry>> and <<drop berry>> messages using the dobjPostAction method, one of Alt’s object reaction methods.

 

redBerry: FoodItem

    location = forestClearing

    noun = 'berry'

    adjective = 'red'

    sDesc = "red berry"

    initial = "A red berry hangs suspended from a vine

        like a single drop of blood."

    lDesc = "The berry gleams darkly ruby-red, leaving you

        to wonder what it is you seem to have forgotten about

        this particular fruit. "

    dobjPostAction = {

        switch(gVerb()) {

            case takeVerb:

                if (self.hasMovedCount == 1)

                    "You pick the berry, neatly cleaving it from the

vine. ";

                else

                    "You pick up the slowly-decaying berry.";

                return true;

            case dropVerb:

                "The berry drops to the ground, battered slightly. ";

                return true;

        }

    }

;

 

The first thing to notice about dobjPostAction is its name. This method is called during the postAction() stage of Command Execution and will be called for redBerry when it is the direct object of a command and the preAction and action stages of Command Execution have been successful.

 

The second thing to note is that dobjPostAction takes no arguments. In order to limit the types of commands we want dobjPostAction to consider we need to use the global command variable functions. In this case we simply check gVerb() to see if the verb is takeVerb or dropVerb.

 

 There are two special attributes that are automatically maintained each time an object is moved: hasMoved and hasMovedCount. The first is a Boolean indicator of whether the object has moved or not. The second counts the number of times an object has moved. This allows an author to vary the message for <<take berry>> based on whether it’s the first time or not.

 

Finally, dobjPostAction returns true for both takeVerb and dropVerb, indicating that the displayed message is to override any prcediing messages resulting from the take or drop action. If dobjPostAction doesn’t return true then any default library messages are first displayed, followed by any messages produced in dobjPostAction.

 

>take berry

You pick the berry, neatly cleaving it from the vine.

 

>drop it

The berry drops to the ground, battered slightly.

 

Suppose we now wish to have a response to the player command <<eat berry>>, making the berry poisonous some percentage of the time and harmless the rest of the time. Again, we could directly override the doEat() method on berry, or we can choose to employ the object reaction’s dobjPreAction() method.

 

redBerry: FoodItem

    location = forestClearing

    noun = 'berry'

    adjective = 'red'

    sDesc = "red berry"

    initial = "A red berry hangs suspended from a vine

        like a single drop of blood."

    lDesc = "The berry gleams darkly ruby-red, leaving you

        to wonder what it is you seem to have forgotten about

        this particular fruit. "

    dobjPreAction = {

        local r;

        randomize;

        switch(gVerb()) {

            case tasteVerb:

                "You extend your tongue tentatively\n";

                return nil;

            case eatVerb:

                r = _rand(100);

                if (r <= 30) {

                    "The tinniest nibble is enough. It was a poisonous

                    berry after all. ";

                    story.deathMessage(nil);

                } else

                    "You nibble at the berry, but the curious taste

                    repels you. ";

                return true;

        }

    }

    dobjPostAction = {

        switch(gVerb()) {

            case takeVerb:

                if (self.hasMovedCount == 1)

                    "You pick the berry, neatly cleaving it from the vine. ";

                else

                    "You pick up the slowly-decaying berry.";

                return true;

            case dropVerb:

                "The berry drops to the ground, battered slightly. ";

                return true;

        }

    }

;

 

The dobjPreAction has a switch statement that checks for tasteVerb and eatVerb. Because the logic for the taseVerb case returns nil preAction processing is not terminated, and instead control continues to the action stage of Command Execution.

 

>taste berry

You extend your tongue tentatively

You taste nothing unexpected.

 

Eating the berry, unfortunately, has fatal results.

 

>eat berry

The tinniest nibble is enough. It was a poisonous berry after all.

 

*** You have died ***

 

In a total of 0 minutes, you have achieved a score of 0 points out of a possible 100.

You may restore a saved game, start over, quit, or undo the current command.

Please enter RESTORE, RESTART, QUIT, or UNDO: >

 

In fact, you’ll notice, if you choose UNDO that eating the berry always results in death for the player. This is because the random number generator hasn’t been properly randomised. To do this we add the following code to our story object.

 

modify story

    startingLocation = forestClearing

    title = "VENTURE"

    headline = "An Interactive Worked Example\n

        Copyright (c) 2000 by Kevin Forchione\n"

    introduction = "\b\b\b\bDays of cutting through impenetrable

        underbrush have brought you to the brink of exhaustion. At last

        your efforts bear fruit as you stumble upon the remnants of a

        lost civilisation."

    initDaemon = {

        randomize;

        notify(queue, &processQueue, 0);   

        setdaemon(turnCount, nil);         // start the turn counter daemon

        setdaemon(sleepDaemon, nil);               // start the sleep daemon

        setdaemon(eatDaemon, nil);                // start the hunger daemon

        gameClock.setClock('5:00 am', 5, 1887, 6, 17);       

    }

;

 

We’ve copied the initDaemon() method from story object (story.t in the objects folder) and added the randomise statement to the method. This method also sets the queue daemon that produces aggregated display; the daemons for turn and time advancement (turnCount), fatigue (sleepDaemon), hunger (eatDaemon); as well as setting the gameClock to the default time of 5:00 a.m, June 17th 1887 (The starting date and time of Infocom’s Sherlock: The Riddle of the Crown Jewels).

 

Now when we eat the berry we get "The tinniest nibble is enough. It was a poisonous berry after all. " displayed roughly 30% of the time and "You nibble at the berry, but the curious taste repels you. " about 70% of the time. Because we return in the latter case we skip the Action stage of Command Execution and the berry remains in our inventory.

 

The next object in VENTURE is a handy rucksack that is capable of tidying away objects automatically once our own carrying capacity has been exceeded.

 

ruckSack: SackItem

    location = forestClearing

    noun = 'sack' 'rucksack' 'bag'

    adjective = 'ruck'

    sDesc = "rucksack"

    aDesc = "your <<self.sDesc>>"

    theDesc = "your <<self.sDesc>>"

    lDesc = "This capacious sack can hold a surprising number of

        objects."

    whenOpen = "\^<<self.theDesc>> is here, lying open."

;

 

The rucksack is a SackItem class object, which has a default maxBulk of 100, more than enough for the average adventurer. A SackItem object starts out open by default, and aside from the usual sDesc and lDesc we’ve given it a whenOpen description attribute.

 

The whenOpen is displayed when the object is displayed in room descriptions, but before the object has moved. Once the object has moved the whenOpen, like the initial, is no longer displayed.

 

Forest Clearing

       This small clearing is covered in a thick bed of grasses and heavy frond leaves of the trees that form a near- impenetrable barrier encircling it. The air is heavy and sweltering, seemingly drawing out swarms of tiny blue-bottle flies that dart about drawn to the rotting vegetation.

       Your rucksack is here, lying open.

       A red berry hangs suspended from a vine like a single drop of blood.

 

>take sack

Taken.

 

>drop it

Dropped.

 

>l

Forest Clearing

       This small clearing is covered in a thick bed of grasses and heavy frond leaves of the trees that form a near- impenetrable barrier encircling it. The air is heavy and sweltering, seemingly drawing out swarms of tiny blue-bottle flies that dart about drawn to the rotting vegetation.

       A red berry hangs suspended from a vine like a single drop of blood.

       You can also see your rucksack here.

 

The rucksack can be worn, like any ClothingItem class object and by default must be worn in order to tidy away objects.

 

>put on rucksack

(First taking your rucksack)

Okay, you're now wearing your rucksack.

 

The “(Fist taking your rucksack)” is the display of an implicit command, in this case an implicit <<take rucksack>> has been done before the rucksack is worn. More will be said about this later.

 

Another common object definition follows.

 

stoneSteps: Passageway

    location = forestClearing

    noun = 'steps'

    adjective = 'stone'

    isThem = true

    sDesc = "stone steps"

    lDesc = "Ancient steps extend down into shadows. Perhaps these steps

        have have lain untrodden for a thousand years."

    doorDest = stoneChamber

;

 

The Passageway class derives from the more basic Obstacle class and defines a one-way passage between two locations. The important elements of this definition are the isThem attribute, which tells the library to refer to this object as a plural; and the doorDest, which indicates where the actor will go when he passes through the obstacle.

 

We end the chapter with one more Room definition, for completeness.

 

stoneChamber: Room

    sDesc = "Stone Chamber"

    lDesc = "The sunken chamber appears to be hewn from the living rock

        itself. It forms a perfect square ten yards on each side.

        Sunlight filters down from the steps above, casting the furthest

        corners of the chamber into deepening shadow."

;